package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"grab-train-ticket/grab-ticket4go/consts/redisCons"
	//	"grab-train-ticket/grab-ticket4go/utils/lock"
	"grab-train-ticket/grab-ticket4go/utils/redis"
	"grab-train-ticket/grab-ticket4go/utils/resp"
	"grab-train-ticket/grab-ticket4go/vo"
	"regexp"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
)

type TicketController struct {
	beego.Controller
}

func init() {
	//	logs.EnableFuncCallDepth(true)//输出调用的文件名和文件行号
	logs.Async()                //为了提升性能, 可以设置异步输出
	logs.SetLogFuncCallDepth(3) //调整方法栈深度，以便正确获取打印日志的代码行号
}

var mu sync.Mutex

func (c *TicketController) TraintInit() {
	var ticketInitVo vo.TicketInitVo
	defer PanicDeal(c, nil)                                //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketInitVo) //解析请求json报文
	logs.Info("请求参数：", ticketInitVo)
	if ticketInitVo.SeatList == "" {
		panic(resp.Common_paramIlegal + "SeatList格式正确示例(最大值)：9x23x99，表示：每排五个座位9一节车厢23排x一列火车99节车厢")
	} else {
		if ok, _ := regexp.MatchString("^[1-9][x][0-9]{2}[x][0-9]{1,2}$", ticketInitVo.SeatList); !ok {
			panic(resp.Common_paramIlegal + "SeatList格式正确示例(最大值)：9x26x99，表示：每排五个座位9一节车厢23排x一列火车99节车厢")
		}
		if a, _ := strconv.Atoi(strings.Split(ticketInitVo.SeatList, "x")[1]); a > 26 {
			panic(resp.Common_paramIlegal + "SeatList车厢最多26排")
		}
		if a, _ := strconv.Atoi(strings.Split(ticketInitVo.SeatList, "x")[2]); a == 0 {
			panic(resp.Common_paramIlegal + "SeatList车厢数不能为0")
		}
	}
	if ticketInitVo.StopStation == "" {
		panic(resp.Common_paramIIncomplete + "StopStation")
	}
	if ticketInitVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	var seatGroup = [26]string{"A", "B", "C", "D", "E", "F", "G", "H", "Y", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
		"S", "T", "U", "V", "W", "X", "Y", "Z"}
	logs.Info("入参基础校验、业务校验通过", seatGroup)
	seatListStr := ""
	iLenth, _ := strconv.Atoi(strings.Split(ticketInitVo.SeatList, "x")[2])
	jLenth, _ := strconv.Atoi(strings.Split(ticketInitVo.SeatList, "x")[1])
	kLenth, _ := strconv.Atoi(strings.Split(ticketInitVo.SeatList, "x")[0])
	logs.Info("#iLenth:", iLenth, "#jLenth:", jLenth, "#kLenth:", kLenth)
	for i := 1; i < iLenth+1; i++ {
		for j := 0; j < jLenth; j++ {
			for k := 1; k < kLenth+1; k++ {
				seatListStr += strconv.Itoa(i) + "-" + seatGroup[j] + strconv.Itoa(k) + ","
			}
		}
	}
	ticketInitVo.SeatList = seatListStr[0:strings.LastIndex(seatListStr, ",")]
	logs.Info("#SeatList:", ticketInitVo.SeatList)
	redis.Set(redisCons.TrainInfoKey+ticketInitVo.TrainId, ticketInitVo, 0)
	// 移除列车所有购票信息(购票历史订单，持久化在订单表)
	var seatListArray []string = strings.Split(ticketInitVo.SeatList, ",")
	for _, seat := range seatListArray {
		seatKey := redisCons.TicketPurchaseList + ticketInitVo.TrainId + "-" + seat
		redis.Del(seatKey)
	}
	logs.Info("#redis.ticketInitVo:%s", redis.Get(redisCons.TrainInfoKey+ticketInitVo.TrainId))
	c.Data["json"] = resp.RespBase(true)
	c.ServeJSON()

}

//@router /purchasestest [get]  //注解使得：/purchasestest可以访问，自动路由使得：/ticket/purchases也可以访问
func (c *TicketController) Purchases() {
	var ticketPurchaseVo vo.TicketPurchaseVo
	defer PanicDeal(c, nil) //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketPurchaseVo)
	logs.Info("请求参数：", ticketPurchaseVo)
	if ticketPurchaseVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	if ticketPurchaseVo.StartStation == "" {
		panic(resp.Common_paramIIncomplete + "StartStation")
	}
	if ticketPurchaseVo.EndStation == "" {
		panic(resp.Common_paramIIncomplete + "EndStation")
	}
	if ticketPurchaseVo.PurchaseCount == 0 {
		panic(resp.Common_paramIIncomplete + "PurchaseCount")
	}
	// 不用查询是否有余票，前端一般已经调了余票查询接口，所以直接购票即可，竞争失败再提示失败
	if ticketPurchaseVo.PurchaseCount > 1 {
		// 多人购票（需要优先连坐，再看是否优先靠窗）连坐抢票算法TODO
	} else {
		// 单人购票（无需考虑连坐）
		// 获取列车座位list信息
		ticketInfoStr := redis.Get(redisCons.TrainInfoKey + ticketPurchaseVo.TrainId)
		var ticketInfoMap map[string]string
		json.Unmarshal([]byte(ticketInfoStr), &ticketInfoMap)
		seatListStr := ticketInfoMap["seatList"]
		stopStationStr := ticketInfoMap["stopStation"]
		// 获取用户购票经停站
		userStopStations := stopStationStr[strings.Index(stopStationStr, ticketPurchaseVo.StartStation):strings.Index(stopStationStr, ticketPurchaseVo.EndStation)] + ticketPurchaseVo.EndStation
		if userStopStations == "" {
			panic(resp.Ticket_voyageError)
		}
		seatListArray := strings.Split(seatListStr, ",")
		defer mu.Unlock()
		mu.Lock() // 互斥锁只能解决单节点部署下的并发问题，如果是服务多节点部署，必须要用分布式锁ex：redlock
		for _, seat := range seatListArray {
			seatKey := redisCons.TicketPurchaseList + ticketPurchaseVo.TrainId + "-" + seat
			oldSeatStations := redis.Get(seatKey)
			if oldSeatStations != "" {
				if !strings.Contains(oldSeatStations, ticketPurchaseVo.StartStation) && !strings.Contains(oldSeatStations, ticketPurchaseVo.EndStation) {
					redis.Set(seatKey, oldSeatStations+","+userStopStations, 0)
					logs.Info("#购票成功-复坐,#oldSeatStations:[%s],#userStopStations:[%s]", oldSeatStations,
						userStopStations)
					// 和核心抢票、退票、无关的，统一异步完成（异步记录购票人信息，以及对应订单信息）TODO
					c.Data["json"] = resp.RespData(seat, "", "")
					c.ServeJSON()
					return
				}
			} else {
				redis.Set(seatKey, userStopStations, 0)
				logs.Info("#购票成功-新坐,#userStopStations:[%s]", userStopStations)
				c.Data["json"] = resp.RespData(seat, "", "")
				c.ServeJSON()
				return
			}
		}
	}
	panic(resp.Ticket_noSeat)
}

func (c *TicketController) Refund() {
	var ticketPurchaseVo vo.TicketPurchaseVo
	defer PanicDeal(c, nil) //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketPurchaseVo)
	logs.Info("请求参数：", ticketPurchaseVo)
	if ticketPurchaseVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	if ticketPurchaseVo.PurchaseSeat == "" {
		panic(resp.Common_paramIIncomplete + "PurchaseSeat")
	}
	if ticketPurchaseVo.StartStation == "" {
		panic(resp.Common_paramIIncomplete + "StartStation")
	}
	if ticketPurchaseVo.EndStation == "" {
		panic(resp.Common_paramIIncomplete + "EndStation")
	}
	seatKey := redisCons.TicketPurchaseList + ticketPurchaseVo.TrainId + "-" + ticketPurchaseVo.PurchaseSeat
	oldSeatStations := redis.Get(seatKey)
	if oldSeatStations == "" {
		panic(resp.Ticket_repeatRefund)
	}
	logs.Info("#oldSeatStations:[%s]", oldSeatStations)
	var oldSeatStationsArray []string
	oldSeatStationsArray = strings.Split(oldSeatStations, ",")
	newSeatStations := ""
	flag := false
	for _, oldSeatStation := range oldSeatStationsArray {
		logs.Info("#{},#{},#{}-{}", strings.Index(oldSeatStation, ticketPurchaseVo.StartStation),
			strings.LastIndex(oldSeatStation, ticketPurchaseVo.EndStation), strings.Count(oldSeatStation, ""),
			strings.Count(ticketPurchaseVo.EndStation, ""))
		if strings.Index(oldSeatStation, ticketPurchaseVo.StartStation) == 0 && strings.LastIndex(oldSeatStation, ticketPurchaseVo.EndStation) == strings.Count(oldSeatStation, "")-strings.Count(ticketPurchaseVo.EndStation, "") {
			flag = true
		} else {
			newSeatStations += oldSeatStation + ","
		}
	}
	if flag {
		newSeatStationsStr := ""
		if len(oldSeatStationsArray) == 1 {
			newSeatStationsStr = newSeatStations
		} else {
			newSeatStationsStr = newSeatStations[0 : strings.Count(newSeatStations, "")-1]
		}
		redis.Set(seatKey, newSeatStationsStr, 0)
		logs.Info("#退票成功,#oldSeatStations:[%s];#newSeatStations:[{}]", oldSeatStations, newSeatStations)
		c.Data["json"] = resp.RespData(newSeatStations, "", "")
		c.ServeJSON()
		return
	} else {
		logs.Info("#退票失败,#oldSeatStations:[{}];#newSeatStations:[{}]", oldSeatStations, newSeatStations)
		panic(resp.Ticket_repeatRefund)
	}
}

func (c *TicketController) RemainTicket() {
	var ticketRemainVo vo.TicketRemainVo
	defer PanicDeal(c, nil) //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketRemainVo)
	logs.Info("请求参数：", ticketRemainVo)
	if ticketRemainVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	if ticketRemainVo.StartStation == "" {
		panic(resp.Common_paramIIncomplete + "StartStation")
	}
	if ticketRemainVo.EndStation == "" {
		panic(resp.Common_paramIIncomplete + "EndStation")
	}
	ticketInfoStr := redis.Get(redisCons.TrainInfoKey + ticketRemainVo.TrainId)
	// 获取列车座位list信息
	var ticketInfoMap map[string]string
	json.Unmarshal([]byte(ticketInfoStr), &ticketInfoMap)
	seatListStr := ticketInfoMap["seatList"]
	logs.Info("#seatListStr:", seatListStr)
	// 遍历已经购票的所有座位，是否和当前查询行程冲突，（当前行程余票数=列车座位数-已购座位冲突数）
	seatListArray := strings.Split(seatListStr, ",")
	purchasedCount := 0
	for _, seat := range seatListArray {
		key := redisCons.TicketPurchaseList + ticketRemainVo.TrainId + "-" + seat
		seatStation := redis.Get(key)
		//		logs.Info("#key:[%s] #seatStation:[%s]", key, seatStation)
		if seatStation != "" && (strings.Contains(seatStation, ticketRemainVo.StartStation) || strings.Contains(seatStation, ticketRemainVo.EndStation)) {
			purchasedCount++
		}
	}
	logs.Info("#seatListArray.length:[%s],#purchasedCount:[%s]", len(seatListArray), purchasedCount)
	ticketRemainVo.RemainCount = len(seatListArray) - purchasedCount
	c.Data["json"] = ticketRemainVo
	c.ServeJSON()
}

func (c *TicketController) StopStation() {
	var ticketInitVo vo.TicketInitVo
	defer PanicDeal(c, nil) //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketInitVo)
	logs.Info("请求参数：", ticketInitVo)
	if ticketInitVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	ticketInfoStr := redis.Get(redisCons.TrainInfoKey + ticketInitVo.TrainId)
	var ticketInfoMap map[string]string
	json.Unmarshal([]byte(ticketInfoStr), &ticketInfoMap)
	c.Data["json"] = ticketInfoMap["stopStation"]
	c.ServeJSON()
}

func (c *TicketController) SeatList() {
	var ticketInitVo vo.TicketInitVo
	defer PanicDeal(c, nil) //不同栈深度的所有异常统一defer捕获处理：组装异常响应json报文
	json.Unmarshal(c.Ctx.Input.RequestBody, &ticketInitVo)
	logs.Info("请求参数：", ticketInitVo)
	if ticketInitVo.TrainId == "" {
		panic(resp.Common_paramIIncomplete + "TrainId")
	}
	ticketInfoStr := redis.Get(redisCons.TrainInfoKey + ticketInitVo.TrainId)
	var ticketInfoMap map[string]string
	json.Unmarshal([]byte(ticketInfoStr), &ticketInfoMap)
	c.Data["json"] = ticketInfoMap["seatList"]
	c.ServeJSON()
}

//异常统一处理：组装异常响应json报文PanicDeal(controller实列，响应data)
func PanicDeal(c *TicketController, data interface{}) {
	if err := recover(); err != nil { //无论发生非预期异常，还是预期内的业务异常都统一响应json报文
		errString := fmt.Sprint(err)
		logs.Error(errString)
		debug.PrintStack() //打印异常堆栈，便于快速定位事故地点
		logs.Info("#开始组装异常响应json报文")
		resp2 := resp.BuildServicePanic(errString)
		resp2.Data = data
		if c != nil {
			c.Data["json"] = resp2
			c.ServeJSON()
		}
	}
}
